package com.qd.panda.service.third.etc;

import cn.hutool.core.text.UnicodeUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.PemUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.crypto.symmetric.AES;
import com.cdqidi.util.DateUtil;
import com.cdqidi.util.JacksonUtil;
import com.cdqidi.util.SpringContextHolder;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 〈一句话功能简述〉<br>
 *
 * @author sjk
 * @create 2023-10-30 14:47
 * @since 1.0.0
 */
@Service
@Slf4j
public class EtcSignUtil {
    //    private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSvOaDUedNlcwMbAwZd0TBHGScGlPv5EdmZwNYfXP+QhnNs+WTe2QpbA2TnZeYiwSYvV1yV+g9FHXvbyhXIQTZlqYPJ5vKQct8ANVbF3Avqm6QRuMof4s58WOMRnA4vObbwAp2AoB0DybKabwC9viObVdOMoLb4IGUIm8yTLLoYQIDAQAB";
//    private static final String PRIVATE_KEY = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAO84pSRIbL+ds5TtbmO2Y5b6sEdtlzkEQwshjPr+DxrxacuuMNnpCh9HG6Yp5jYc1Yf6QqFoJCB4a1cEm3HApgj+bXOeR81YPF/QaMj3ESAsTr+vc14btU03+OSOfDMwlEzNjES4KT0kqxQeEEIx39zU4XAx0LCYd0i69vWfrpGZAgMBAAECgYEAn2kH4x3AGhBk0yFFJn0r8Dgtr+t/kicc5hX9dGRIfJPTBD+Xnh6E+ELSUfAp0HV2dZ53OEfFiz2zidEwJKVT/fBxKnwsEp47RUtnRlv1e7Qv2kbHuzu3tmaWJuqGnd9JhSOpGOHYLzuNTqDvD4KeNmAyLNkETXOCe49nDMw5+YECQQD7le8KktOetqd2pMBPBBTYT0r6u0/lYKFx9Q+7yCjBraKyhXO0lVgJKzCA56JgiGoMuXP5CZoeEoYvWivMBXkFAkEA82ssVgxlGc9rH07JMEItpT8B9jg1NWwnKg/x5N26ZUqGY1lfOrhTefdpam6KzIZWa3EEpOkwgYWjiWShYlKKhQJBAIXXSNkWLXCk+Ugb85bVtAUzgc8LsUB0/M50Mc+Td2M57XENn/V08eXGzUH2eKLkT46/ZlwZaJ0Q+8PbM6uRScUCQQCU41rtDwvP67TkI3af0/rDdnSmu96f3JVf/vzANndKeXkVz5aV/iThF1odRZwUWZ8GfMyQainHL4zF/w1PbJlZAkAc0vVAzem1K9ome3ePh4RmLQaMflzYD74tRtRTFaL+dVs1vSK5m/nUAFCZSoN9/JpVPOa6Y3wQ7MF5wtHwXe3P";
    @Getter
    private static RSA rsa;
    private static Sign sign;
    private static ResourceLoader resourceLoader;

    @SneakyThrows
    public static void init() {
        if (null != rsa) {
            return;
        }
        String activeProfile = SpringContextHolder.getActiveProfile();
//        log.debug("EtcSignUtil,activeProfile: {}", activeProfile);
        String privateKeyPath;
        String publicKeyPath;
        if (activeProfile.equals(SpringContextHolder.PROFILE_OFFICIAL)) {
            privateKeyPath = "classpath:/rsa/official/privatekey.pem";
        } else {
            privateKeyPath = "classpath:/rsa/prod/privatekey.pem";
        }
        if (activeProfile.equals(SpringContextHolder.PROFILE_OFFICIAL)) {
            publicKeyPath = "classpath:/rsa/official/WF_rsa_public_key_gw.pem";
        } else {
            publicKeyPath = "classpath:/rsa/prod/WF_rsa_public_key_gw_test.pem";
        }
        log.info("EtcSignUtil,私钥地址: {}", privateKeyPath);
        log.info("EtcSignUtil,公钥地址: {}", publicKeyPath);
        Resource resource = resourceLoader.getResource(privateKeyPath);
        PrivateKey privateKey;
        PublicKey publicKey;
        try (InputStream inputStream = resource.getInputStream()) {
            privateKey = PemUtil.readPemPrivateKey(inputStream);
        }
        resource = resourceLoader.getResource(publicKeyPath);
        try (InputStream inputStream = resource.getInputStream()) {
            publicKey = PemUtil.readPemPublicKey(inputStream);
        }
        rsa = new RSA(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue(), privateKey, publicKey);
//        log.debug("rsa: {}", rsa);
        sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKey.getEncoded(), publicKey.getEncoded());
//        log.debug("sign: {}", sign);
    }

    @Autowired
    public EtcSignUtil(ResourceLoader resourceLoader){
        EtcSignUtil.resourceLoader = resourceLoader;
    }

    @SneakyThrows
    public static String createReqJsonStr(JsonNode bizContent,String orgCode) {
        Map<String, Object> param = new HashMap<>(8);
        //加入默认参数
        param.put("orgCode", orgCode);
        param.put("timestamp", DateUtil.localDateTimeToStr(LocalDateTime.now()));
        param.put("version", "1.0");

        String value = createBizContent(bizContent);
        param.put("bizContent", value);

        //2.除去数组中的空值和签名参数
        Map<String, String> newParam = paraFilter(param);
        //3.把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串 + key
        final String linkString = createLinkString(newParam);
        if (log.isDebugEnabled()) {
            log.debug("原始字符串：{}", linkString);
        }
        //4.生成签名串MD5
        String signStr = SecureUtil.md5(linkString).toUpperCase();//16进制  UTF-8  大写
        if (log.isDebugEnabled()) {
            log.debug("MD5生成待签名串:{}", signStr);
        }
        signStr = bytesToBase64(sign.sign(signStr));
        if (log.isDebugEnabled()) {
            log.debug("生成签名值sign: {}", signStr);
        }
        //5.请求报文加上 sign
        newParam.put("sign", signStr);
        newParam.remove("bizContent");

        final ObjectNode s = (ObjectNode) JacksonUtil.getObjectMapper().readTree(JacksonUtil.getObjectMapper().writeValueAsString(newParam));
        s.set("bizContent", bizContent);

        //6.生成请求报文
        String jsonString = s.toString();
        if (log.isDebugEnabled()) {
            log.debug("请求报文：{}", jsonString);
        }
        return jsonString;
    }

    /**
     * 判断签名是否正确
     *
     * @param responseJsonStr key
     * @return true or false
     */
    @SneakyThrows
    public static boolean isSign(String responseJsonStr) {
        Map<String, Object> param = new HashMap<>(8);
        JsonNode jsonNode = JacksonUtil.getObjectMapper().readTree(responseJsonStr);
        JsonNode msgJsonNode = jsonNode.get("msg");
        String respSign = jsonNode.get("sign").asText();
        if (!StringUtils.hasLength(respSign)) {
            log.error("请求参数中sign为空");
            return false;
        }
        param.put("code", jsonNode.get("code").asText());
        if (null != msgJsonNode) {
            param.put("msg", msgJsonNode.asText());
        }
        param.put("receiveTime", jsonNode.get("receiveTime").asText());
        param.put("sign", respSign);
        JsonNode bizContentNode = jsonNode.get("bizContent");
        if (!bizContentNode.isEmpty()) {
            String value = createBizContent(bizContentNode);
            param.put("bizContent", value);
        }
        param.replaceAll((k, v) -> UnicodeUtil.toUnicode(param.get(k).toString()));
        //2.除去数组中的空值和签名参数
        Map<String, String> newParam = paraFilter(param);
        //3.把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
        final String linkString = createLinkString(newParam);
        if (log.isDebugEnabled()) {
            log.debug("【MD5】加密前原始串：{}", linkString);
        }
        //4.生成签名串MD5
        String signStr = SecureUtil.md5(linkString).toUpperCase();//16进制  UTF-8  大写
        if (log.isDebugEnabled()) {
            log.debug("【MD5】加密后sign:{}", signStr);
        }
        return sign.verify(signStr.getBytes(), base64ToBytes(respSign));
    }

    @NotNull
    private static String createBizContent(JsonNode bizContentNode) {
        String value = bizContentNode.toString().replace("\"", "");
        value = value.replace(":", "=");
        value = value.replace(",", "&");
        return value;
    }

    /**
     * 除去数组中的空值和签名参数
     *
     * @param sArray 签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    @SneakyThrows
    private static Map<String, String> paraFilter(Map<String, Object> sArray) {

        Map<String, String> result = new HashMap<>();

        if (sArray == null || sArray.isEmpty()) {
            return result;
        }
        for (String key : sArray.keySet()) {
            Object obj = sArray.get(key);
            String value;
            if (obj instanceof String) {
                value = String.valueOf(obj);
            } else {
                value = JacksonUtil.getObjectMapper().writeValueAsString(obj);
            }
            if ("null".equals(value) || value == null || value.isEmpty() || key.equalsIgnoreCase("sign")
                    || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }

        return result;
    }

    /**
     * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
     *
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    private static String createLinkString(Map<String, String> params) {

        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        StringBuilder preStr = new StringBuilder();
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
                preStr.append(key).append("=").append(value);
            } else {
                preStr.append(key).append("=").append(value).append("&");
            }
        }
        return preStr.toString();
    }

    public static String aes(String key, String json) {
        AES aes = new AES(Mode.ECB, Padding.ZeroPadding, HexUtil.decodeHex(key));
        return aes.encryptBase64(json);
    }

    public static String des(String key, String json) {
        AES aes = new AES(Mode.ECB, Padding.ZeroPadding, HexUtil.decodeHex(key));
        return aes.decryptStr(json);
    }

    public static void main(String[] args) {


//        //生成公私钥对
//        KeyPair pair = SecureUtil.generateKeyPair("RSA");
//        PrivateKey privateKey = pair.getPrivate();
//        PublicKey publicKey = pair.getPublic();
//        //获得私钥
//        String privateKeyStr = bytesToBase64(privateKey.getEncoded());
//        System.out.println("私钥：" + privateKeyStr);
//        //获得公钥
//        String publicKeyStr = bytesToBase64(publicKey.getEncoded());
//        System.out.println("公钥：" + publicKeyStr);


//        AES aes = new AES(Mode.ECB, Padding.ZeroPadding, HexUtil.decodeHex("3ec26fbb5d1a8baa2776f6453cbf13ef"));
//        String encrypt = aes.encryptBase64("1a23bcd中文_测试_汉字");
//        System.out.println(encrypt);


//        System.out.println("RSA.decrypt(\"aa\",KeyType.PrivateKey) = " + RSA.encrypt("aa", KeyType.PrivateKey));
    }

    /**
     * 字节数组转Base64编码
     *
     * @param bytes 字节数组
     * @return Base64编码
     */
    public static String bytesToBase64(byte[] bytes) {
        byte[] encodedBytes = Base64.getEncoder().encode(bytes);
        return new String(encodedBytes, StandardCharsets.UTF_8);
    }

    /**
     * Base64编码转字节数组
     *
     * @param base64Str Base64编码
     * @return 字节数组
     */
    public static byte[] base64ToBytes(String base64Str) {
        byte[] bytes = base64Str.getBytes(StandardCharsets.UTF_8);
        return Base64.getDecoder().decode(bytes);
    }
}